<?php
/**
 * 缓存
 *
 */
class Cache {
	private $_sPath;
	private $_iTime;
	private $_iLevel;
	private $_oCacheExt;
	
	/**
	 * 构造函数
	 *
	 */
	public function __construct() {
		self::_setTime ();
		self::_setLevel ();
		self::_setType ();
	}
	
	private function _setPath($sPath = null) {
		try {
			if ($sPath == null) {
				$sConfPath = Base::getConf ( 'cache.path' );
			} else {
				$sConfPath = $sPath;
			}
			$sRealPath = realpath ( $sConfPath );
			if (! $sRealPath) {
				$sRealPath = realpath ( ROOT . '/' . $sConfPath );
			}
			if ($sPath != null) {
				if (! $sRealPath) {
					throw new ExceptionExt ( '缓存目录' . $sConfPath . '不存在' );
				}
				if (! is_writable ( $sRealPath )) {
					throw new ExceptionExt ( '缓存目录' . $sConfPath . '不可写' );
				}
			}
			$this->_sPath = $sRealPath;
		} catch ( ExceptionExt $e ) {
			$e->showError ( true );
		}
	}
	
	private function _setTime() {
		$this->_iTime = intval ( Base::getConf ( 'cache.time' ) );
	}
	
	private function _setLevel() {
		$this->_iLevel = intval ( Base::getConf ( 'cache.level' ) );
	}
	
	private function _setType() {
		switch (Base::getConf ( 'cache.type' )) {
			case 'apc' :
				$this->_oCacheExt = Base::loadClass ( 'Apc' );
				break;
			case 'memcached' :
				$this->_oCacheExt = Base::loadClass ( 'Memcached' );
				break;
			case 'sqlite' :
				$this->_oCacheExt = Base::loadClass ( 'Sqlitecache' );
				break;
			case 'mysql' :
				break;
			default :
				self::_setPath ();
				$this->_oCacheExt = null;
		}
	}
	
	/**
	 * 设置缓存
	 * $sKey缓存名
	 * $sValue缓存内容
	 * $sCategory缓存所属类型
	 *
	 * @param string $sKey
	 * @param string $sValue
	 * @param string $sCategory
	 */
	public function set($sKey, $sValue, $sCategory = null, $iTime = null) {
		if ($this->_oCacheExt) {
			return $this->_oCacheExt->set ( $sKey, $sValue, $sCategory, $iTime );
		}
		
		$sKey = md5 ( $sKey );
		
		if ($sCategory === null) {
			$sPath = $this->_sPath;
			if ($iTime === null) {
				$iTime = $this->_iTime;
			}
			$iLevel = $this->_iLevel;
		} else {
			$sCategory = strtolower ( $sCategory );
			$sPath = $this->_sPath . '/' . $sCategory;
			if ($iTime === null) {
				$iTime = Base::getConf ( $sCategory . '.cache_time' );
			}
			$iLevel = Base::getConf ( $sCategory . '.cache_level' );
			if ($iTime === null) {
				$iTime = $this->_iTime;
			}
			if ($iLevel === null) {
				$iLevel = $this->_iLevel;
			}
		}
		
		if ($iTime > 0) {
			$iTime += time ();
		}
		
		$sFile = self::_getFile ( $sPath, $sKey, $iLevel );
		$aData = array ('time' => $iTime, 'value' => $sValue );
		
		if (! is_dir ( dirname ( $sFile ) )) {
			$oFile = Base::loadClass ( 'File' );
			$oFile->md ( dirname ( $sFile ), 0777 );
		}
		
		file_put_contents ( $sFile, serialize ( $aData ) );
	}
	
	/**
	 * 获取缓存
	 *
	 * @param string $sKey
	 * @param string $sCategory
	 */
	public function get($sKey, $sCategory = null) {
		if ($this->_oCacheExt) {
			return $this->_oCacheExt->get ( $sKey, $sCategory );
		}
		$sKey = md5 ( $sKey );
		
		if ($sCategory === null) {
			$sPath = $this->_sPath;
			$iLevel = $this->_iLevel;
		} else {
			$sCategory = strtolower ( $sCategory );
			$sPath = $this->_sPath . '/' . $sCategory;
			$iLevel = Base::getConf ( $sCategory . '.cache_level' );
			if ($iLevel === null) {
				$iLevel = $this->_iLevel;
			}
		}
		
		$sFile = self::_getFile ( $sPath, $sKey, $iLevel );
		
		if (is_file ( $sFile )) {
			$aData = unserialize ( file_get_contents ( $sFile ) );
			if ($aData ['time'] == 0) {
				return $aData ['value'];
			} elseif ($aData ['time'] < time ()) {
				@unlink ( $sFile );
				return '__NOCACHE__';
			} else {
				return $aData ['value'];
			}
		} else {
			return '__NOCACHE__';
		}
	}
	
	public function del($sKey, $sCategory = null) {
		if ($this->_oCacheExt) {
			return $this->_oCacheExt->del ( $sKey, $sCategory );
		}
		$sKey = md5 ( $sKey );
		
		if ($sCategory === null) {
			$sPath = $this->_sPath;
			$iLevel = $this->_iLevel;
		} else {
			$sCategory = strtolower ( $sCategory );
			$sPath = $this->_sPath . '/' . $sCategory;
			$iLevel = Base::getConf ( $sCategory . '.cache_level' );
			if ($iLevel === null) {
				$iLevel = $this->_iLevel;
			}
		}
		
		$sFile = self::_getFile ( $sPath, $sKey, $iLevel );
		
		if (is_file ( $sFile )) {
			@unlink ( $sFile );
		}
		return true;
	}
	
	public function clear($sCategory = null) {
		if ($this->_oCacheExt) {
			return $this->_oCacheExt->clear ( $sCategory );
		}
		
		if ($sCategory == null) {
			$oFile = Base::loadClass ( 'File' );
			$aFile = $oFile->ls ( $this->_sPath );
			if (is_array ( $aFile )) {
				foreach ( $aFile as $sRow ) {
					$oFile->rm ( $sRow );
				}
			}
		} else {
			if (is_array ( $sCategory )) {
				foreach ( $sCategory as $sRow ) {
					$this->clear ( $sRow );
				}
			} else {
				$oFile = Base::loadClass ( 'File' );
				$oFile->rm ( $this->_sPath . '/' . $sCategory );
			}
		}
		return true;
	}
	
	private function _getFile($sPath, $sKey, $iLevel) {
		for($i = 0; $i < $iLevel; $i ++) {
			$aPath [] = substr ( $sKey, $i * 2, 2 );
		}
		if (is_array ( $aPath )) {
			return $sPath . '/' . join ( '/', $aPath ) . '/' . $sKey . '.cache';
		} else {
			return $sPath . '/' . $sKey . '.cache';
		}
	}
}
?>